package com.zly.bscloud.utils.recommendation;

import com.zly.bscloud.entity.Article;
import com.zly.bscloud.entity.Preferences;
import com.zly.bscloud.entity.User;

import java.util.*;

/**
 * 描述：对文章点评过用1表示，0代表没点评过。
 * 给目标用户推荐相似度最高用户喜欢的文章
 */
public class ItemCFDemo {

    //系统用户
    private static String[] users;
    //和这些用户相关的电影
    private static String[] movies;
    //用户点评电影情况
    private static Integer[][] allUserMovieCommentList;
    //用户点评电影情况，行转列
    private static Integer[][] allMovieCommentList;
    //电影相似度
    private static HashMap<String,Double> movieABSimilaritys=null;
    //待推荐电影相似度列表
    private static HashMap<Integer,Object> movieSimilaritys=null;
    //用户所在的位置
    private static Integer targetUserIndex=null;
    //目标用户点评过的电影
    private static List<Integer> targetUserCommentedMovies=null;
    //推荐电影
    private static  List<Map.Entry<Integer, Object>> recommlist=null;

    public static List<Article> handle(String user_id,List<User> userList, List<Article> articleList, List<Preferences> preferencesList){
        List<Article> recommendationList = new ArrayList<Article>();
        int userSize = userList.size();
        int articleSize = articleList.size();
        int preferencesSize = preferencesList.size();
        Map<String,Integer> userMap= new HashMap<String,Integer>();
        Map<String,Integer> articleMap= new HashMap<String,Integer>();

        users = new String[userSize];
        movies = new String[articleSize];
        allUserMovieCommentList = new Integer[userSize][articleSize];
        allMovieCommentList = new Integer[articleSize][userSize];

        for(int i=0;i<userSize;i++){
            users[i] = userList.get(i).getId();
            userMap.put(userList.get(i).getId(), i);
        }
        for(int i=0;i<articleSize;i++){
            movies[i] = articleList.get(i).getId();
            articleMap.put(articleList.get(i).getId(), i);
        }
        for(int i=0;i<userSize;i++){
            for(int j=0;j<articleSize;j++){
                allUserMovieCommentList[i][j] = 0;
            }
        }
        for(int i=0;i<preferencesSize;i++){
            allUserMovieCommentList[userMap.get(preferencesList.get(i).getUser_id())][articleMap.get(preferencesList.get(i).getArticle_id())] = 1;
        }

        targetUserIndex = userMap.get(user_id);
        //转换目标用户电影点评列表
        targetUserCommentedMovies=Arrays.asList(allUserMovieCommentList[targetUserIndex]);
        //System.out.println(targetUserCommentedMovies);
        //计算电影相似度
        calcAllMovieSimilaritys();
        //获取全部待推荐电影
        calcRecommendMovie();
        //输出推荐电影
        //System.out.print("推荐电影列表：");
        int k=0;
        for (Map.Entry<Integer, Object> item:recommlist){
            if(k>5){
                break;
            }
            k++;
            //System.out.print(movies[item.getKey()]+"  ");
            //System.out.println(item.getKey());
            //System.out.println(articleList.get(item.getKey()));
            recommendationList.add(articleList.get(item.getKey()));
        }
        //System.out.println();
        return recommendationList;
    }

    /**
     * 获取全部推荐电影
     */
    private static void calcRecommendMovie(){
        movieSimilaritys=new HashMap<>();
        for (int i=0;i<targetUserCommentedMovies.size()-1;i++){
            for (int j=i+1;j<targetUserCommentedMovies.size();j++){
                Object similarity=null;
                if(targetUserCommentedMovies.get(i)==1 && targetUserCommentedMovies.get(j)==0 && ( movieABSimilaritys.get(i+""+j)!=null || movieABSimilaritys.get(j+""+i)!=null)){
                    similarity=movieABSimilaritys.get(i+""+j)!=null?movieABSimilaritys.get(i+""+j):movieABSimilaritys.get(j+""+i);
                    movieSimilaritys.put(j,similarity);
                }else if(targetUserCommentedMovies.get(i)==0 && targetUserCommentedMovies.get(j)==1 && (movieABSimilaritys.get(i+""+j)!=null || movieABSimilaritys.get(j+""+i)!=null)){
                    similarity=movieABSimilaritys.get(i+""+j)!=null?movieABSimilaritys.get(i+""+j):movieABSimilaritys.get(j+""+i);
                    movieSimilaritys.put(i,similarity);
                }
            }
        }

        recommlist = new ArrayList<Map.Entry<Integer, Object>>(movieSimilaritys.entrySet());
        Collections.sort(recommlist, new Comparator<Map.Entry<Integer, Object>>() {
            @Override
            public int compare(Map.Entry<Integer, Object> o1, Map.Entry<Integer, Object> o2) {
                return o1.getValue().toString().compareTo(o2.getValue().toString());
            }
        });

        //System.out.println("待推荐相似度电影列表："+recommlist);
    }

    /**
     * 计算全部物品间的相似度
     */
    private static void calcAllMovieSimilaritys(){
        converRow2Col();
        movieABSimilaritys=new HashMap<>();
        for (int i=0;i<allMovieCommentList.length-1;i++){
            for (int j=i+1;j<allMovieCommentList.length;j++){
                movieABSimilaritys.put(i+""+j,calcTwoMovieSimilarity(allMovieCommentList[i],allMovieCommentList[j]));
            }
        }

        //System.out.println("电影相似度："+movieABSimilaritys);
    }


    /**
     * 根据电影全部点评数据，计算两个电影相似度
     * @param movie1Stars
     * @param movie2Starts
     * @return
     */
    private static double calcTwoMovieSimilarity(Integer[] movie1Stars,Integer[] movie2Starts){
        float sum=0;
        for(int i=0;i<movie1Stars.length;i++){
            //System.out.println(movie1Stars[i]+" "+movie2Starts[i]);
            sum+=Math.pow(movie1Stars[i]-movie2Starts[i],2);
        }
        return Math.sqrt(sum);
    }

    /**
     * 数组行转列
     */
    private static void converRow2Col(){
        for (int i=0;i<allUserMovieCommentList[0].length;i++){
            for(int j=0;j<allUserMovieCommentList.length;j++){
                allMovieCommentList[i][j]=allUserMovieCommentList[j][i];
            }
        }
        //System.out.println("电影点评转行列："+Arrays.deepToString(allMovieCommentList));
    }

    /**
     * 查找用户所在的位置
     * @param user
     * @return
     */
    private static Integer getUserIndex(String user){
        if(user==null || "".contains(user)){
            return null;
        }

        for(int i=0;i<users.length;i++){
            if(user.equals(users[i])){
                return i;
            }
        }

        return null;
    }
}
